#pragma once

#include <string>
#include <iostream>
#include <strings.h>
#include <functional>
/*网络必要的头文件*/
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

/*其他操作系统接口*/
#include <unistd.h>

namespace server
{
    using namespace std;
    enum {SOCKET_ERROR = 1,START_ERROR,BIND_ERROR};
    typedef function<void(int,struct sockaddr_in &,string &)> func_t;
    class udpServer
    {
    public:
        /*服务器不需要绑定任何IP！
         *绑定默认的0.0.0.0即可
         *绑定了一个指定的IP之后，只能接收到指定IP的客户端的数据*/
        udpServer(const func_t &func,const uint16_t &port,const string &ip = defaultIp)
            :_func(func),_port(port),_ip(ip),_socketFd(-1)
        {
            /*以IP协议、数据报的形式打开文件
             *也就是以UDP协议打开网络文件
             *如果打开失败，说明无法进行网络通信，程序退出*/
            _socketFd = socket(AF_INET,SOCK_DGRAM,0);
            if(_socketFd == -1)
            {
                cerr << "socket fail" << endl;
                exit(SOCKET_ERROR);
            }
            cout << "socket success: " << _socketFd << endl; 

            /*以后对网络的I/O的操作就是对网络文件操作
             *但是还没有将该文件绑定IP和端口号，也就是还没有绑定套接字
             *所以现在要绑定套接字*/
            struct sockaddr_in local = getSockaddr_in();
            int n = bind(_socketFd,(struct sockaddr *)&local,sizeof(local));
            if(n == -1)
            {
                cerr << "bind fail" << endl;
                exit(BIND_ERROR);
            }
            /*至此套接字创建工作完成*/
        }
        ~udpServer()
        {}

        void start()
        {
            while(true)
            {
                /*读取客户端发送的数据
                 *并打印出来，相当于服务器后台日志*/
                char buffer[buffer_num];

                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int n = recvfrom(_socketFd,buffer,sizeof(buffer)-1,0,(struct sockaddr *)&client,&len);
                if(n > 0)
                {
                    buffer[n] = 0;
                    string message = buffer;
                    string clinetIp = inet_ntoa(client.sin_addr);
                    uint16_t clinetPort = ntohs(client.sin_port);

                    cout << clinetIp << "[" << clinetPort << "]# " << message << endl;

                    /*回调*/
                    _func(_socketFd,client,message);
                }
            }
        }
    private:
        /*参与网络通信需要端口号和ip地址
         *端口号是一个2字节的整数
         *ip地址用点分十进制的字符串表示*/
        uint16_t _port;
        string _ip;

        /*以文件的形式打开一个网络文件
         *即使用socket()打开一个网络文件*/
        int _socketFd;

        /*所有对象共享一个默认的IP*/
        static const string defaultIp;
        static const int buffer_num = 1024;

        /*回调函数:服务器要处理的业务逻辑*/
        func_t _func;

        /*获取struct sockaddr结构体*/
        struct sockaddr_in getSockaddr_in()
        {
             struct sockaddr_in local;
            /*初始化sockaddr_in对象
             *并且将端口号、IP地址设置进去
             *我们使用的IP地址是字符串，所以需要转换成整数
             *使用inet_addr()接口，里面自动转换成整数，并且自动大小端字节序*/
            bzero((void *)&local,sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            //local.sin_addr.s_addr = inet_addr(_ip.c_str());
            local.sin_addr.s_addr = INADDR_ANY;
            return local;
        }
    };
    const string udpServer::defaultIp = "0.0.0.0";
} /*namespace server ends here*/